﻿using Desktop.Interfaces;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.Serialization;

namespace Desktop.Library
{
    /// <summary>
    /// A collection of cloneable, observable objects.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable()]
    public class ObservableList<T> : IList<T>, INotifyPropertyChanged, ICloneable<ObservableList<T>>, ISerializable
        where T : class, ICloneable<T>
    {
        #region Events

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Constructors

        public ObservableList()
            : base()
        {
            InnerList = new List<T>();
        }

        /// <summary>
        /// Clones the list and everything in it.
        /// </summary>
        /// <param name="list"></param>
        public ObservableList(ObservableList<T> list)
        {
            InnerList = new List<T>();
            foreach (T item in list)
            {
                Add(item.Clone());
            }
        }

        public ObservableList(SerializationInfo info, StreamingContext context)
        {
            List<T> list = info.GetValue("InnerList", typeof(List<T>)) as List<T>;
            foreach (T item in list)
            {
                Add(item);
            }
        }

        #endregion

        #region Properties

        private List<T> InnerList { get; set; }

        public int Count
        {
            get
            {
                return InnerList.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        #endregion

        #region Methods

        private void PropertyHasChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public ObservableList<T> Clone()
        {
            return new ObservableList<T>(this);
        }

        object ICloneable.Clone()
        {
            return new ObservableList<T>(this);
        }

        public int IndexOf(T item)
        {
            return InnerList.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            InnerList.Insert(index, item);
            PropertyHasChanged("Item");
        }

        public void RemoveAt(int index)
        {
            InnerList.RemoveAt(index);
            PropertyHasChanged("Item");
        }

        public T this[int index]
        {
            get
            {
                return InnerList[index];
            }
            set
            {
                if (InnerList[index] != value)
                {
                    InnerList[index] = value;
                    PropertyHasChanged("Item");
                }
            }
        }

        public void Add(T item)
        {
            InnerList.Add(item);
            PropertyHasChanged("Item");
        }

        public void Clear()
        {
            InnerList.Clear();
            PropertyHasChanged("Item");
        }

        public bool Contains(T item)
        {
            return InnerList.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            InnerList.CopyTo(array, arrayIndex);
            PropertyHasChanged("Item");
        }

        public bool Remove(T item)
        {
            bool success = InnerList.Remove(item);
            PropertyHasChanged("Item");
            return success;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return InnerList.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return InnerList.GetEnumerator();
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("InnerList", InnerList);
        }

        #endregion
    }
}
